package com.icesoft.base.manager.base;

import com.icesoft.base.manager.helper.PageParam;
import com.icesoft.base.manager.suppose.NotUpdatePart;
import com.icesoft.core.common.exception.CheckException;
import com.icesoft.core.common.util.ReflectionUtils;
import com.icesoft.core.dao.base.BaseDao;
import com.icesoft.core.dao.base.BaseModel;
import com.icesoft.core.dao.criteria.FromBuilder;
import com.icesoft.core.dao.criteria.Page;
import com.icesoft.core.dao.criteria.QueryBuilder;
import com.icesoft.core.dao.criteria.SelectBuilder;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @author XHH
 */
public abstract class BaseService<T extends BaseModel> extends BaseDao<T> {

    public void save(T model) {
        if (model.getId() == null || model.getId() == 0) {
            create(model);
        } else {
            update(model);
        }
    }

    @Transactional
    public void save(Collection<T> models) {
        models.forEach(this::save);
    }


    public void delete(int id) {
        T m = load(id);
        if (m != null) {
            delete(m);
        }
    }

    public void delete(int[] ids) {
        assert ids != null;
        List<T> list = new ArrayList<>();
        for (int id : ids) {
            T model = load(id);
            if (model != null) {
                list.add(model);
            }
        }
        delete(list);
    }


    /**
     * 更新model中不为NULL且没有NotUpdatePart注解的数据
     *
     * @param model 用来携带要更新的部分数据
     */
    public void updateNotNullProperties(T model) {
        Integer id = model.getId();
        T dbModel = load(id);
        if (dbModel == null) {
            throw new CheckException("数据更新失败，找不到id为" + id + "的数据");
        }
        copyTo(model, dbModel);
        update(dbModel);
    }

    private void copyTo(T model, T dbModel) {
        BeanWrapper modelBeanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(model);
        BeanWrapper dbModelBeanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(dbModel);
        List<Field> fields = ReflectionUtils.getDeclaredFields(model);
        for (Field field : fields) {
            Object value = modelBeanWrapper.getPropertyValue(field.getName());
            if (value != null) {
                if (field.isAnnotationPresent(NotUpdatePart.class)) {
                    continue;
                }
                //&& modelBeanWrapper.getPropertyType(field.getName()) == int.class
                if (value.equals(0) && field.getName().endsWith("Id")) {
                    continue;
                }
                if (dbModelBeanWrapper.isWritableProperty(field.getName())) {
                    dbModelBeanWrapper.setPropertyValue(field.getName(), value);
                }
            }
        }
    }

    /**
     * 如果是根据id倒序排序的分页，根据上一次查询的记录的id优化分页查询
     *
     * @param queryBuilder 查询条件
     * @param pageParam    分页条件
     * @apiNote 将会自动添加id倒序排序
     **/
    public Page<T> page(QueryBuilder queryBuilder, PageParam pageParam) {
        return page(SelectBuilder.selectFrom(queryBuilder.getFromBuilder()), pageParam);
    }

    /**
     * 如果是根据id倒序排序的分页，根据上一次查询的记录的id优化分页查询DTO条件
     *
     * @param selectBuilder 查询DTO条件
     * @param pageParam     分页条件
     * @apiNote 将会自动添加id倒序排序
     **/
    public <D> Page<D> page(SelectBuilder selectBuilder, PageParam pageParam) {
        Integer lastEndId = pageParam.getLastEndId();
        Integer lastEndOffset = pageParam.getLastEndOffset();
        int offset = pageParam.getOffset();
        int limit = pageParam.getLimit() <= 0 ? 1000 : pageParam.getLimit();
        FromBuilder fromBuilder = selectBuilder.getFromBuilder();
        fromBuilder.offsetLimit(offset, limit);
        if (pageParam.getPage() > 0) {
            fromBuilder.page(pageParam.getPage(), pageParam.getLimit());
        }
        Page<D> page = new Page<>();
        int count = this.count(fromBuilder);
        page.setTotal(count);
        if (count == 0) {
            return page;
        }
        if (!fromBuilder.order().isEmpty()) {
            page.setData(this.findSelect(selectBuilder));
            return page;
        }
        fromBuilder.order().desc("id");
        if (offset == 0 || lastEndId == null || lastEndOffset == null) {
            List<D> list = this.findSelect(selectBuilder);
            page.setData(list);
            return page;
        }
        boolean isForward = offset >= lastEndOffset;
        if (isForward) {
            // offset增大的情况
            int calcOffset = offset - lastEndOffset;
            int calcOffsetFormEnd = count - offset - limit;
            if (calcOffsetFormEnd <= calcOffset) {
                // 离尾页近的情况，从尾页开始算
                isForward = false;
                if (calcOffsetFormEnd > 0) {
                    fromBuilder.order().asc("id").end().offsetLimit(calcOffsetFormEnd, limit);
                } else {
                    int newLimit = calcOffsetFormEnd + limit;
                    if (newLimit <= 0) {
                        newLimit = limit;// offset>count，最后一页数据删除完了，直接查看新的最后一页
                    }
                    // 最后一页
                    fromBuilder.order().asc("id").end().offsetLimit(0, newLimit);
                }
            } else {
                fromBuilder.where().andLt("id", lastEndId).end().order().desc("id").end().offsetLimit(calcOffset,
                        limit);
            }
        } else {
            // offset减少的情况
            int calcOffset = lastEndOffset - offset - limit - 1;
            if (calcOffset < offset) {
                fromBuilder.where().andGt("id", lastEndId).end().order().asc("id").end().offsetLimit(calcOffset, limit);
            } else {
                // 离首页近的情况，直接查询
                fromBuilder.offsetLimit(offset, limit).order().desc("id").end();
                isForward = true;
            }

        }
        List<D> list = this.findSelect(selectBuilder);
        if (!isForward) {
            Collections.reverse(list);
        }
        page.setData(list);
        return page;
    }

}
